/******************************************************************************
Desc:
  A simple tetris game for DOS. Compiled by Turbo C 2.0.
	compile command: tcc tetris graphics.lib
  This program is only a sample, anyone can distribute it in anyway. :-)

Auth:
  Kang Kai (siaview@163.com) 2002-09-03

******************************************************************************/

/*   Tetris.c --- the main source file.   */

#include <dos.h>
#include <math.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <graphics.h>

#include "tetris.h"

/* Variables for graphics initialization. */
int    graphdriver;        /* The Graphics device driver        */
int    graphmode;        /* The Graphics mode value        */
double aspect_ratio;        /* Aspect ratio of a pixel on the screen*/
int    max_x, max_y;        /* The maximum resolution of the screen */
int    max_colors;        /* The maximum # of colors available    */
int    error_code;        /* Reports any graphics errors        */
struct palettetype palette;        /* Used to read palette info    */

/* New block position */
XY_POS startPos;
/* The info of current active block are stored here */
CUR_BLOCK_SHAPE curBlock;
/* The info of the next block are stored here */
CUR_BLOCK_SHAPE nextBlock;

/* Main pane cells */
int cell[BLOCK_PER_ROW+1][ROW_COUNT+1];

/* Each type of block has it's own color */
int block_colors[7] =
{
    RED, LIGHTCYAN, LIGHTGRAY, GREEN, BLUE, LIGHTMAGENTA, YELLOW
};

/* player score */
int score;
int eliminated_lines;

void initgame(void);
void init_graph(void);
void newblock();
void initblock(CUR_BLOCK_SHAPE* pBlock);
void copy_block(CUR_BLOCK_SHAPE* pDest, CUR_BLOCK_SHAPE* pSrc);
void draw_cur_block(int erase);
void draw_next_block(int erase);
int movedown_cur_block();
int moveleft_cur_block();
int moveright_cur_block();
int can_rotate_cur_block();
void rotate_cur_block();
void draw_row(int rowno, int hilite);
void draw_score();
void remove_row(int rowno);
void update_pane();
int can_place_block(CUR_BLOCK_SHAPE* pBlock);

int main()
{
    int tx, ty, i, n;
    int command;
    char buf[16];
    
    init_graph();
    
    /* Draw the main pane border */
    setcolor(RED);
    setlinestyle( SOLID_LINE, 0, THICK_WIDTH );
    rectangle(PANE_LEFT - THICK_WIDTH, PANE_TOP - THICK_WIDTH,
		PANE_RIGHT + THICK_WIDTH, PANE_BOTTOM + THICK_WIDTH);
    setlinestyle( SOLID_LINE, 0, NORM_WIDTH );

    /* Draw preview pane */
    setcolor(YELLOW);
    setlinestyle( SOLID_LINE, 0, THICK_WIDTH - 1);
    rectangle(PREVIEW_PANE_LEFT, PREVIEW_PANE_TOP,
		      PREVIEW_PANE_RIGHT, PREVIEW_PANE_BOTTOM);
    setlinestyle( SOLID_LINE, 0, NORM_WIDTH );

	/* Draw tip text */
    setcolor(WHITE);
    tx = PREVIEW_PANE_LEFT + 10;
	ty = PANE_TOP;

	outtextxy(tx, ty, "Next Block:");
	ty += 200;

    outtextxy(tx, ty, " Q : Exit");
	ty += 20;
    
    outtextxy(tx, ty, " F2 : New Game");
	ty += 20;

    outtextxy(tx, ty, " \x1b : Left");
	ty += 20;

    outtextxy(tx, ty, " \x1a : Right");
   	ty += 20;

    outtextxy(tx, ty, " \x18 : Rotate"); /* \x18 --- up arrow */
	ty += 20;

    outtextxy(tx, ty, " \x19 : Fast Move Down"); /* \x18 --- up arrow */
	ty += 20;

    outtextxy(tx, ty, " Space : Drop Down");
	ty += 20;

    if(getch() == 'q')
	{
		closegraph();
		exit(0);
	}

    initgame();
    
    n = 0;
    
    for(;;)
    {
        while(!kbhit()) /* keyboard is not hit */
        {
            n++;
            delay(30000);

            if((n % 5) == 0) /* default interval between move down block */
            {
                if(movedown_cur_block() == 0) /* can't movedown */
                {
                    /* set the cells info */
                    for(i = 0; i < 4; i++)
                    {
                        cell[curBlock.Blocks[i].x][curBlock.Blocks[i].y] =
							curBlock.nBlockType;
                    }

					/* remove full filled rows */
					update_pane();
					
					newblock();
				}
            }
        };

		/* parse and process the keyboard input */
        command = getch();
    
        switch(command)
        {
            case 0: /* arrow key maybe pressed */
                command = getch();
                switch(command)
                {
					case 60: /* F2, new game */
						initgame();
						break;
                    case 72: /* up, rotate */
                        if(can_rotate_cur_block())
                            rotate_cur_block();
                        break;
                    case 75: /* left */
                        moveleft_cur_block();
                        break;
                    case 77: /* right*/
                        moveright_cur_block();
                        break;
                    case 80: /* down */
                        movedown_cur_block();
                        break;
                }
                break;
            
            case 'q': /* quit game */
			case 'Q':
			case 27: /* ESC */
                closegraph(); /* Return the system to text mode    */
                exit(0);
            
            case ' ': /* space, drop down the block */
                while(movedown_cur_block() != 0) /* repeat movedown */
				{
				};
				
				/* set the cells */
                for(i = 0; i < 4; i++)
                {
                    cell[curBlock.Blocks[i].x][curBlock.Blocks[i].y] =
						curBlock.nBlockType;
                }

     			/* remove full rows */
				update_pane();
				newblock();

				break;
			case 'n': /* start a new game */
			/*	initgame(); */
				break;
            default:
                break;
        };
    };
}

void init_graph(void)
{
    int xasp, yasp;            /* Used to read the aspect ratio*/
    
    graphdriver = DETECT;         /* Request auto-detection    */
    initgraph( &graphdriver, &graphmode, "" );
    error_code = graphresult();        /* Read result of initialization*/
    if( error_code != grOk ){        /* Error occured during init    */
        printf(" Graphics System Error: %s\n", grapherrormsg( error_code ) );
        exit( 1 );
    }
    
    getpalette( &palette );        /* Read the palette from board    */
    max_colors = getmaxcolor() + 1;    /* Read maximum number of colors*/
    
    max_x = getmaxx();
    max_y = getmaxy();            /* Read size of screen        */
    
    getaspectratio( &xasp, &yasp );    /* read the hardware aspect    */
    aspect_ratio = (double)xasp / (double)yasp; /* Get correction factor    */
}

void initgame(void)
{
    int i, j;
    /* 1 based */
    startPos.x = 4;
    startPos.y = 1;

	randomize();

    for(i = 0; i <= BLOCK_PER_ROW; i++)
        for(j = 0; j <= ROW_COUNT; j++)
            cell[i][j] = CELL_EMPTY;

	/* clear main pane */
	for(i = 1; i <= ROW_COUNT; i++)
		draw_row(i, 0);

	/* clear preview pane */
	setfillstyle(SOLID_FILL, BLACK);
    bar(PREVIEW_PANE_LEFT, PREVIEW_PANE_TOP,
		      PREVIEW_PANE_RIGHT, PREVIEW_PANE_BOTTOM);
    setcolor(YELLOW);
    setlinestyle( SOLID_LINE, 0, THICK_WIDTH - 1);
    rectangle(PREVIEW_PANE_LEFT, PREVIEW_PANE_TOP,
		      PREVIEW_PANE_RIGHT, PREVIEW_PANE_BOTTOM);

    /* init the first block */
    initblock(&curBlock);
    draw_cur_block(DRAW_BLOCK_NORMAL); /* normal draw */
    /* init the next block */
    initblock(&nextBlock);
	draw_next_block(DRAW_BLOCK_NORMAL);

	/* reset score */
	score = 0;
	eliminated_lines = 0;
	draw_score();
};

void newblock()
{
	char c;
	/* copy the block */
	copy_block(&curBlock, &nextBlock);
	draw_cur_block(DRAW_BLOCK_NORMAL); /* normal draw */
	
	/* init the next block */
	draw_next_block(DRAW_BLOCK_ERASE);
	/* if curBlock can't be placed, game over */
	if(can_place_block(&curBlock))
	{
		initblock(&nextBlock);
		draw_next_block(DRAW_BLOCK_NORMAL);
	}
	else /* write game over info */
	{
		setcolor(YELLOW);
		outtextxy(PREVIEW_PANE_LEFT + 15, PREVIEW_PANE_TOP + 20, "Game Over");
		setcolor(LIGHTGREEN);
		outtextxy(PREVIEW_PANE_LEFT + 15, PREVIEW_PANE_TOP + 140, "Start a new game(Y/N)?");
		c = getch(); 
		while(c != 'y' && c != 'Y' && c != 'n' && c != 'N')
		{
			c = getch();
		}

		if(c == 'y' || c == 'Y')
		{
			setfillstyle(SOLID_FILL, BLACK);
			bar(PREVIEW_PANE_LEFT + 15, PREVIEW_PANE_TOP + 140,
				PREVIEW_PANE_LEFT + 300, PREVIEW_PANE_TOP + 160);
		
			initgame();
		}
		else
		{
			closegraph();
			exit(0);
		}
	}
};

void initblock(CUR_BLOCK_SHAPE* pBlock)
{
    int i;
	int type = random(100) % 7;
    pBlock->nBlockType = type;
    pBlock->rotate_status = 0;
    switch(type)
    {
        case 0: /* #### */
            for(i = 0; i < 4; i++)
            {
                pBlock->Blocks[i].x = startPos.x + i;
                pBlock->Blocks[i].y = startPos.y;
            }
            break;
        case 1: /* ## */
                /* ## */
            pBlock->Blocks[0].x = startPos.x + 1;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 2;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 1;
            pBlock->Blocks[2].y = startPos.y + 1;
            pBlock->Blocks[3].x = startPos.x + 2;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        case 2: /* ### 123 */
                /*  #   4  */
            pBlock->Blocks[0].x = startPos.x + 1;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 2;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 3;
            pBlock->Blocks[2].y = startPos.y;
            pBlock->Blocks[3].x = startPos.x + 2;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        case 3: /* ##  12  */
                /*  ##  34 */
            pBlock->Blocks[0].x = startPos.x + 1;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 2;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 2;
            pBlock->Blocks[2].y = startPos.y + 1;
            pBlock->Blocks[3].x = startPos.x + 3;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        case 4: /*  ##  12 */
                /* ##  34  */
            pBlock->Blocks[0].x = startPos.x + 2;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 3;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 1;
            pBlock->Blocks[2].y = startPos.y + 1;
            pBlock->Blocks[3].x = startPos.x + 2;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        case 5: /* ### 123 */
                /* #   4   */
            pBlock->Blocks[0].x = startPos.x + 1;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 2;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 3;
            pBlock->Blocks[2].y = startPos.y;
            pBlock->Blocks[3].x = startPos.x + 1;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        case 6: /* ### 123 */
                /*   #   4 */
            pBlock->Blocks[0].x = startPos.x + 1;
            pBlock->Blocks[0].y = startPos.y;
            pBlock->Blocks[1].x = startPos.x + 2;
            pBlock->Blocks[1].y = startPos.y;
            pBlock->Blocks[2].x = startPos.x + 3;
            pBlock->Blocks[2].y = startPos.y;
            pBlock->Blocks[3].x = startPos.x + 3;
            pBlock->Blocks[3].y = startPos.y + 1;
            break;

        default:
            /* error, program can't goes here */
            exit(1);
    }
};

void copy_block(CUR_BLOCK_SHAPE* pDest, CUR_BLOCK_SHAPE* pSrc)
{
	int i;
	pDest->nBlockType = pSrc->nBlockType;
	pDest->rotate_status = pSrc->rotate_status;

	for(i = 0; i < 4; i++)
	{
		pDest->Blocks[i].x = pSrc->Blocks[i].x;
		pDest->Blocks[i].y = pSrc->Blocks[i].y;
	}
};

int can_place_block(CUR_BLOCK_SHAPE* pBlock)
{
	int i;
	for(i = 0; i < 4; i++)
	{
		if( cell[pBlock->Blocks[i].x][pBlock->Blocks[i].y] != CELL_EMPTY )
			return 0;
	}

	return 1;
};

/*
    Draw the current active block
  params:
    erase 0 --- normal draw
          1 --- draw in black color, erase
*/
void draw_cur_block(int erase)
{
    int i;
    short l, t, r, b;

    if(erase == 0) /* normal draw*/
    {
        setfillstyle(SOLID_FILL, block_colors[curBlock.nBlockType]);
        setcolor(DARKGRAY);
    }
    else
    {
        setfillstyle(SOLID_FILL, BLACK);
    }

    for(i = 0; i < 4; i++)
    {
        l = PANE_LEFT + BRICK_SIZE * (curBlock.Blocks[i].x - 1);
        t = PANE_TOP + BRICK_SIZE * (curBlock.Blocks[i].y - 1);
        r = l + BRICK_SIZE - 1;
        b = t + BRICK_SIZE - 1;

        bar(l, t, r, b);
        if(erase == 0) /* draw brick border */
            rectangle(l, t, r, b);
    }
};

void draw_next_block(int erase)
{
    int i;
    short l, t, r, b;

    if(erase == 0) /* normal draw*/
    {
        setfillstyle(SOLID_FILL, block_colors[nextBlock.nBlockType]);
        setcolor(DARKGRAY);
    }
    else
    {
        setfillstyle(SOLID_FILL, BLACK);
    }

    for(i = 0; i < 4; i++)
    {
        l = PREVIEW_PANE_LEFT + BRICK_SIZE * (nextBlock.Blocks[i].x - startPos.x) + 10;
        t = PREVIEW_PANE_TOP + BRICK_SIZE * (nextBlock.Blocks[i].y - startPos.y) + 15;
        r = l + BRICK_SIZE - 1;
        b = t + BRICK_SIZE - 1;

        bar(l, t, r, b);
        if(erase == 0) /* draw brick border */
            rectangle(l, t, r, b);
    }
};

/*
    return 0 --- can't movedown
           1 --- movedown
*/
int movedown_cur_block()
{
    int i;
    for(i = 0; i < 4; i++)
    {
        if(curBlock.Blocks[i].y == ROW_COUNT || /* reach bottom */
           (cell[curBlock.Blocks[i].x][curBlock.Blocks[i].y + 1])
            != CELL_EMPTY) /* have brick */
            return 0;
    }
    draw_cur_block(DRAW_BLOCK_ERASE); /* erase first */
    for(i = 0; i < 4; i++)
    {
        curBlock.Blocks[i].y++;
    }
    draw_cur_block(DRAW_BLOCK_NORMAL); /* normal draw */
    return 1;
};

int moveleft_cur_block()
{
    int i;
    for(i = 0; i < 4; i++)
    {
        if(curBlock.Blocks[i].x == 1 || /* reach left border */
           (cell[curBlock.Blocks[i].x - 1][curBlock.Blocks[i].y])
            != CELL_EMPTY) /* have brick */
            return 0;
    }
    draw_cur_block(DRAW_BLOCK_ERASE); /* erase first */
    for(i = 0; i < 4; i++)
    {
        curBlock.Blocks[i].x--;
    }
    draw_cur_block(DRAW_BLOCK_NORMAL); /* normal draw */
    return 1;
};

int moveright_cur_block()
{
    int i;
    for(i = 0; i < 4; i++)
    {
        if(curBlock.Blocks[i].x == BLOCK_PER_ROW || /* reach right border */
           (cell[curBlock.Blocks[i].x + 1][curBlock.Blocks[i].y])
            != CELL_EMPTY) /* have brick */
            return 0;
    }
    draw_cur_block(DRAW_BLOCK_ERASE); /* erase first */
    for(i = 0; i < 4; i++)
    {
        curBlock.Blocks[i].x++;
    }
    draw_cur_block(DRAW_BLOCK_NORMAL); /* normal draw */
    return 1;
};

/*
    return 0 --- can't rotate
           1 --- rotate
*/
int can_rotate_cur_block()
{
    int i, j, can_rotate = 0;
    switch(curBlock.nBlockType)
    {
        case 0: /* #### */
            if(curBlock.rotate_status == 0) /* #### */
                can_rotate = (curBlock.Blocks[0].y >= 2) && (curBlock.Blocks[0].y <= ROW_COUNT - 2) &&
                             (cell[curBlock.Blocks[0].x][(curBlock.Blocks[0].y + 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][(curBlock.Blocks[0].y + 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][(curBlock.Blocks[0].y + 2)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][(curBlock.Blocks[0].y - 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][(curBlock.Blocks[0].y + 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][(curBlock.Blocks[0].y + 2)] == CELL_EMPTY);
            else /* | */
                can_rotate = (curBlock.Blocks[0].x >= 3) && /* (curBlock.Blocks[0].y <= ROW_COUNT - 2) && */
                             (cell[curBlock.Blocks[0].x - 2][(curBlock.Blocks[0].y - 2)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x - 1][(curBlock.Blocks[0].y - 2)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x - 1][(curBlock.Blocks[0].y - 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x - 1][(curBlock.Blocks[0].y)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][(curBlock.Blocks[0].y - 2)] == CELL_EMPTY);
            break;

        case 1: /* ## */
                /* ## */
            /* need rotate? :-) */
            break;

        case 2: /* ### */
                /*  #  */

            if(curBlock.Blocks[0].y == 1)
                return 0;
            if(curBlock.rotate_status == 1 && curBlock.Blocks[0].x == 1)
                return 0;
            if(curBlock.rotate_status == 2 && curBlock.Blocks[0].y == ROW_COUNT)
                return 0;
            for(i = 0; i < 3; i++)
                for(j = 0; j < 3; j++)
                    if(cell[curBlock.Blocks[1].x - 1 + i][curBlock.Blocks[1].y - 1 + j] != CELL_EMPTY)
                        return 0;

            can_rotate = 1;
            break;

        case 3: /* ##  */
                /*  ## */

            if(curBlock.rotate_status == 0)
                can_rotate = (curBlock.Blocks[0].y >= 2) && (curBlock.Blocks[0].y <= ROW_COUNT - 2) &&
                             (cell[curBlock.Blocks[0].x][(curBlock.Blocks[0].y + 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x][(curBlock.Blocks[0].y + 2)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][(curBlock.Blocks[0].y)] == CELL_EMPTY);
            else
                can_rotate =/* (curBlock.Blocks[0].x <= ROW_COUNT - 2) &&*/
                             (cell[curBlock.Blocks[0].x + 1][(curBlock.Blocks[0].y)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][(curBlock.Blocks[0].y - 1)] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x][(curBlock.Blocks[0].y - 2)] == CELL_EMPTY);
            break;

        case 4: /*  ## */
                /* ##  */
            if(curBlock.rotate_status == 0)
                can_rotate = (curBlock.Blocks[0].y >= 2) && (curBlock.Blocks[0].y <= ROW_COUNT - 2) &&
                             (cell[curBlock.Blocks[0].x - 1][curBlock.Blocks[0].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][curBlock.Blocks[0].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][curBlock.Blocks[0].y + 2] == CELL_EMPTY);
            else
                can_rotate = (curBlock.Blocks[0].x < ROW_COUNT - 1) &&
                             (cell[curBlock.Blocks[0].x + 2][curBlock.Blocks[0].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 1][curBlock.Blocks[0].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[0].x + 2][curBlock.Blocks[0].y - 1] == CELL_EMPTY);
            break;

        case 5: /* ### */
                /* #   */

            switch(curBlock.rotate_status)
            {
            case 0:
                can_rotate = (curBlock.Blocks[1].y >= 2) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY);
                break;

            case 1:
                can_rotate = (curBlock.Blocks[1].x >= 2) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY);
                break;
            
            case 2:
                can_rotate = (curBlock.Blocks[1].y <= ROW_COUNT - 2) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY);
                break;
            
            case 3:
                can_rotate = /*(curBlock.Blocks[1].x >= 2) &&*/
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY);
                break;
            }

            break;

        case 6: /* ### */
                /*   # */
            switch(curBlock.rotate_status)
            {
            case 0:
                can_rotate = (curBlock.Blocks[1].y >= 2) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY);
                break;

            case 1:
                can_rotate = (curBlock.Blocks[1].x >= 2) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY);
                break;
            
            case 2:
                can_rotate = (curBlock.Blocks[1].y <= ROW_COUNT - 2) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY);
                break;
            
            case 3:
                can_rotate = /*(curBlock.Blocks[1].x >= 2) &&*/
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x - 1][curBlock.Blocks[1].y - 1] == CELL_EMPTY) &&
                             (cell[curBlock.Blocks[1].x + 1][curBlock.Blocks[1].y + 1] == CELL_EMPTY);
                break;
            }
            break;

        default:
            /* error */
            exit(1);
    }
    return can_rotate;
};

void rotate_cur_block()
{
    draw_cur_block(DRAW_BLOCK_ERASE); /* erase first */

    switch(curBlock.nBlockType)
    {
        case 0: /* #### */
            if(curBlock.rotate_status == 0) /* #### */
            {
                curBlock.Blocks[0].x = curBlock.Blocks[3].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y + 2;
                curBlock.Blocks[1].x = curBlock.Blocks[3].x - 1;
                curBlock.Blocks[1].y = curBlock.Blocks[3].y + 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x - 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y - 1;
            }
            else /* | */
            {
                curBlock.Blocks[0].x = curBlock.Blocks[3].x - 2;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y + 1;
                curBlock.Blocks[1].x = curBlock.Blocks[3].x - 1;
                curBlock.Blocks[1].y = curBlock.Blocks[3].y + 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x + 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y + 1;
            }
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 2;
            break;

        case 1: /* ## */
                /* ## */
            /* no rotate :-) */
            break;

        case 2: /* ### */
                /*  #  */
            switch(curBlock.rotate_status)
            {
            case 0:
                curBlock.Blocks[0].x = curBlock.Blocks[3].x;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y;
                curBlock.Blocks[3].x = curBlock.Blocks[2].x;
                curBlock.Blocks[3].y = curBlock.Blocks[2].y;
                curBlock.Blocks[2].x = curBlock.Blocks[1].x;
                curBlock.Blocks[2].y = curBlock.Blocks[1].y - 1;
                break;

            case 1:
                curBlock.Blocks[0].x = curBlock.Blocks[3].x;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y;
                curBlock.Blocks[3].x = curBlock.Blocks[2].x;
                curBlock.Blocks[3].y = curBlock.Blocks[2].y;
                curBlock.Blocks[2].x = curBlock.Blocks[1].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[1].y;
                break;

            case 2:
                curBlock.Blocks[0].x = curBlock.Blocks[3].x;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y;
                curBlock.Blocks[3].x = curBlock.Blocks[2].x;
                curBlock.Blocks[3].y = curBlock.Blocks[2].y;
                curBlock.Blocks[2].x = curBlock.Blocks[1].x;
                curBlock.Blocks[2].y = curBlock.Blocks[1].y + 1;
                break;

            case 3:
                curBlock.Blocks[0].x = curBlock.Blocks[3].x;
                curBlock.Blocks[0].y = curBlock.Blocks[3].y;
                curBlock.Blocks[3].x = curBlock.Blocks[2].x;
                curBlock.Blocks[3].y = curBlock.Blocks[2].y;
                curBlock.Blocks[2].x = curBlock.Blocks[1].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[1].y;
                break;

            }
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 4;
            break;

        case 3: /* ##  */
                /*  ## */

            if(curBlock.rotate_status == 0)
            {
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 2;
                curBlock.Blocks[1].x = curBlock.Blocks[1].x - 1;
                curBlock.Blocks[1].y = curBlock.Blocks[1].y + 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x - 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y - 1;
            }
            else
            {
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 2;
                curBlock.Blocks[1].x = curBlock.Blocks[1].x + 1;
                curBlock.Blocks[1].y = curBlock.Blocks[1].y - 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x + 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y + 1;
            }
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 2;
            break;

        case 4: /*  ## */
                /* ##  */
            if(curBlock.rotate_status == 0)
            {
                curBlock.Blocks[0].x = curBlock.Blocks[0].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 1;
                curBlock.Blocks[1].x = curBlock.Blocks[1].x - 2;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y + 1;
            }
            else
            {
                curBlock.Blocks[0].x = curBlock.Blocks[0].x + 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 1;
                curBlock.Blocks[1].x = curBlock.Blocks[1].x + 2;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y - 1;
            }
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 2;
            break;

        case 5: /* ### */
                /* #   */
            switch(curBlock.rotate_status)
            {
            case 0:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x + 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y - 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x + 2;
                break;
            case 1:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x + 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y + 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y - 2;
                break;
            case 2:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y + 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x - 2;
                break;
            case 3:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y - 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y + 2;
                break;
            }
            
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 4;
            break;

        case 6: /* ### */
                /*   # */
            switch(curBlock.rotate_status)
            {
            case 0:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x + 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y - 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y - 2;
                break;
            case 1:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x + 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x - 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y + 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x - 2;
                break;
            case 2:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y - 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y + 1;
                curBlock.Blocks[3].y = curBlock.Blocks[3].y + 2;
                break;
            case 3:
                curBlock.Blocks[0].x = curBlock.Blocks[0].x - 1;
                curBlock.Blocks[0].y = curBlock.Blocks[0].y + 1;
                curBlock.Blocks[2].x = curBlock.Blocks[2].x + 1;
                curBlock.Blocks[2].y = curBlock.Blocks[2].y - 1;
                curBlock.Blocks[3].x = curBlock.Blocks[3].x + 2;
                break;
            }
            
            curBlock.rotate_status = (curBlock.rotate_status + 1) % 4;
            break;

        default:
            /* error */
            exit(1);
    }

    draw_cur_block(DRAW_BLOCK_NORMAL); /* draw rotated block */

};

/*
  Is the row full filled?
*/
int isfull_row(int rowno)
{
	int i;
	for(i = 1; i <= BLOCK_PER_ROW; i++)
		if(cell[i][rowno] == CELL_EMPTY)
			return 0; /* not full */
	return 1;
};

void draw_row(int rowno, int hilite)
{
    int i;
    short l, t, r, b;
	
	for(i = 1; i <= BLOCK_PER_ROW; i++)
	{
		if(cell[i][rowno] == CELL_EMPTY)
			setfillstyle(SOLID_FILL, BLACK);
		else
			setfillstyle(SOLID_FILL, block_colors[cell[i][rowno]]);

		setcolor(hilite ? WHITE : DARKGRAY); 

		l = PANE_LEFT + BRICK_SIZE * (i - 1);
        t = PANE_TOP + BRICK_SIZE * (rowno - 1);
        r = l + BRICK_SIZE - 1;
        b = t + BRICK_SIZE - 1;

        bar(l, t, r, b);
        if(cell[i][rowno] != CELL_EMPTY)
            rectangle(l, t, r, b);
	}
};

void remove_row(int rowno)
{
	int i, j;

	for(i = rowno; i > 1; i--)
	{
		for(j = 1; j <= BLOCK_PER_ROW; j++)
		{
			cell[j][i] = cell[j][i - 1];
		}
	}
};

void draw_score()
{
	int tx, ty;
	char buf[32];

    tx = PANE_RIGHT + 50;
	ty = PANE_TOP + 120;

	/* clear... */
	setfillstyle(SOLID_FILL, BLACK);
    bar(tx, ty, tx + 150, ty + 50);

	/* Draw scores text */
    setcolor(WHITE);
	sprintf(buf, "Score : %d", score);
	outtextxy(tx, ty, buf);

	sprintf(buf, "Lines : %d", eliminated_lines);
	outtextxy(tx, ty + 20, buf);
	
};

/*
    check the pane, if some rows are full filled,
	remove them and repaint the pane.
	the scores are calculated here.
*/
void update_pane()
{
	int fr[4];
    int i, j, t = 0;
	for(i = 0; i < 4; i++)
		fr[i] = 0;

	/* find full rows */
	for(i = ROW_COUNT; i > 0; i--)
	{
		if(isfull_row(i)) /* mark the row as full */
		{
			fr[t] = i;
			t++;
		}
	}

	/* hilite or flash the full rows for a while... */
	for(i = 0; i < t; i++)
	{
		draw_row(fr[i], 1);
	}

	/* remove the full rows and repaint pane */
	for(i = 0; i < t; i++)
	{
		remove_row(fr[i] + i);
	}
	for(i = 1; i <= ROW_COUNT; i++)
	{
		draw_row(i, 0);
	}

	score += 10;
	switch(t)
	{
	case 0: /* no lines eliminated */
		break;
	case 1: /* 1 lines eliminated */
		score += 100;
		break;
	case 2:
		score += 200;
		break;
	case 3:
		score += 400;
		break;
	case 4:
		score += 800;
		break;
	};

	eliminated_lines += t;

	draw_score();
};
